home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue62 / system / CABAPI.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2000-09-08  |  11.0 KB  |  311 lines

  1. unit CABAPI;
  2.  
  3. interface
  4.  
  5. uses Classes;
  6.  
  7. function  CABIsFile (const CABFileName: String): Boolean;
  8. function  CABIsMultiPart (const CABFileName: String): Boolean;
  9. function  CABGetFileCount (const CABFileName: String): Integer;
  10. procedure CABGetFileList (const CABFileName: String; List: TStringList);
  11. procedure CABExtractFile (const CABFileName, DestPath, FileName: String);
  12. procedure CABExtractMultipleFiles (const CABFileName, DestPath: String; List: TStringList);
  13.  
  14. implementation
  15.  
  16. uses Windows, SysUtils, FileCtrl;
  17.  
  18. const
  19.     // FDI Errors
  20.     FDINone                     =       0;              // ok
  21.     FDICabinetNotFound          =       1;              // bad filename passed to FDICopy
  22.     FDINotACabinet              =       2;              // File not in CAB format
  23.     FDIUnknownCABVersion        =       3;              // unknown CAB file version
  24.     FDICorruptCAB               =       4;              // Cabinet file is corrupt
  25.     FDIAllocFail                =       5;              // Out of memory
  26.     FDIBadCompressType          =       6;              // Unknown compression type
  27.     FDIMDIFail                  =       7;              // decompression error
  28.     FDITargetFile               =       8;              // error writing to dest file
  29.     FDIReserveMismatch          =       9;              // reserve size mismatch
  30.     FDIWrongCabinet             =       10;             // incorrect CAB returned
  31.     FDIUserAbort                =       11;             // user aborted
  32.  
  33.     // FDI notify codes
  34.     ncCABInfo                    =       0;        // General information about cabinet
  35.     ncPartialFile            =       1;        // First file in cabinet is continuation
  36.     ncCopyFile                    =       2;        // File to be copied
  37.     ncCloseFileInfo            =       3;          // close the file, set relevant info
  38.     ncNextCabinet            =       4;            // File continued to next cabinet
  39.     ncEnumerate                    =       5;        // Enumeration status
  40.  
  41. type
  42.     TERF = record
  43.         ErrCode, ErrNo: Integer;
  44.         ErrorPresent: Bool;
  45.     end;
  46.  
  47.     TFDICabinetInfo = record
  48.         cbCabinet: Integer;                             // size of the archive
  49.         cFolders: Word;                                 // number of folders
  50.         cFiles: Word;                                   // number of files
  51.         setID: Word;                                    // application-defined magic #
  52.         iCabinet: Word;                                 // number of cabinet in set
  53.         fReserve: Integer;                              // has reserved area?
  54.         hasprev: Integer;                               // chained to previous?
  55.         hasnext: Integer;                               // chained to next?
  56.     end;
  57.  
  58.     TFDINotification = record
  59.         FileSize: Integer;                // uncomp size of the file (ncCopyFile only)
  60.         FileName: PChar;                // name of a file in the CAB
  61.         psz2: PChar;
  62.         psz3: PChar;
  63.         AppValue: Pointer;                    // application supplied value
  64.         fd: Integer;                        // file handle
  65.         Date: Word;                        // file's 16-bit FAT date
  66.         Time: Word;                        // file's 16-bit FAT time
  67.         Attribs: Word;                        // file's 16-bit FAT attributes
  68.         setID: Word;                        // application-defined magic #
  69.         iCabinet: Word;                        // number of this CAB
  70.         iFolder: Word;                        // number of current 'folder'
  71.         FDIError: Integer;                // error code, if any
  72.     end;
  73.  
  74.  
  75. var
  76.     erf: TERF;
  77.     CABLib: HModule;
  78.     Info: TFDICabinetInfo;
  79.     DestinationPath: String;
  80.     FDICreate: function (pAlloc, pFree, pOpen, pRead, pWrite, pClose, pSeek: Pointer; cpuType: Integer; var erf: TERF): THandle; cdecl;
  81.     FDIDestroy: function (h: THandle): Bool; cdecl;
  82.     FDIIsCabinet: function (h: THandle; fd: Integer; var info: TFDICabinetInfo): Bool; cdecl;
  83.     FDICopy: function (h: THandle; CabName, CabPath: PChar; Flags: Integer; pNotify, pEncrypt, pUser: Pointer): Bool; cdecl;
  84.  
  85. // These are the callback routines used by FDI/FCI interface
  86.  
  87. function MyAlloc (Bytes: Integer): Pointer; cdecl;
  88. begin
  89.     Result := AllocMem (Bytes);
  90. end;
  91.  
  92. function MyFree (P: Pointer): Pointer; cdecl;
  93. begin
  94.     FreeMem (P);
  95.     Result := Nil;
  96. end;
  97.  
  98. function MyOpen (FileName: PChar; Mode: Integer): Integer; cdecl;
  99. begin
  100.     Result := _lopen (FileName, Mode);
  101. end;
  102.  
  103. function MyClose (fd: Integer): Integer; cdecl;
  104. begin
  105.     Result := _lclose (fd);
  106. end;
  107.  
  108. function MyRead (fd: Integer; buff: Pointer; bytes: Integer): Integer; cdecl;
  109. begin
  110.     Result := _lread (fd, buff, bytes);
  111. end;
  112.  
  113. function MyWrite (fd: Integer; buff: Pointer; bytes: Integer): Integer; cdecl;
  114. begin
  115.     Result := _lwrite (fd, buff, bytes);
  116. end;
  117.  
  118. function MySeek (fd: Integer; pos, mode: Integer): Integer; cdecl;
  119. begin
  120.     Result := _llseek (fd, pos, mode);
  121. end;
  122.  
  123. // These routines simplify the otherwise baroque API
  124.  
  125. function NewFDIContext: THandle;
  126. begin
  127.     Result := FDICreate (@MyAlloc, @MyFree, @MyOpen, @MyRead, @MyWrite, @MyClose, @MySeek, 0, erf);
  128. end;
  129.  
  130. function CABIsFile (const CABFileName: String): Boolean;
  131. var
  132.     fd: Integer;
  133.     Context: THandle;
  134. begin
  135.     Result := False;
  136.     Context := NewFDIContext;
  137.     if Context <> 0 then try
  138.         fd := MyOpen (PChar (CABFileName), of_Read);
  139.         if fd <> -1 then try
  140.             Result := FDIIsCabinet (Context, fd, info);
  141.         finally
  142.             MyClose (fd);
  143.         end;
  144.     finally
  145.         FDIDestroy (Context);
  146.     end;
  147. end;
  148.  
  149. function CABIsMultiPart (const CABFileName: String): Boolean;
  150. begin
  151.     Result := False;
  152.     if CABIsFile (CABFileName) then Result := (Info.iCabinet > 0) or (Info.hasPrev <> 0) or (Info.hasNext <> 0);
  153. end;
  154.  
  155. function CABGetFileCount (const CABFileName: String): Integer;
  156. begin
  157.     Result := 0;
  158.     if CABIsFile (CABFileName) then Result := Info.cFiles;
  159. end;
  160.  
  161. function GetFileListCallback (NotifyType: Integer; var Info: TFDINotification): Integer; cdecl;
  162. var
  163.     S: String;
  164.     List: TStringList;
  165. begin
  166.     Result := 0;
  167.     List := Info.AppValue;
  168.     if NotifyType = ncCopyFile then begin
  169.         S := Info.FileName;
  170.         S := S + '|' + IntToStr (Info.FileSize);
  171.         S := S + '|' + IntToStr (MakeLong (Info.Time, Info.Date));
  172.         List.Add (S);
  173.     end;
  174. end;
  175.  
  176. procedure CABGetFileList (const CABFileName: String; List: TStringList);
  177. var
  178.     Context: THandle;
  179. begin
  180.     List.Clear;
  181.     if CABIsFile (CABFileName) then begin
  182.         Context := NewFDIContext;
  183.         if Context <> 0 then try
  184.             FDICopy (Context, PChar (ExtractFileName (CABFileName)),
  185.                               PChar (ExtractFilePath (CABFileName)),
  186.                               0, @GetFileListCallback, Nil, List);
  187.         finally
  188.             FDIDestroy (Context);
  189.         end;
  190.     end;
  191. end;
  192.  
  193. function FileExtractSingleCallback (NotifyType: Integer; var Info: TFDINotification): Integer; cdecl;
  194. var
  195.     Path: String;
  196.     TargetFile: PChar;
  197.     FileTime: TFileTime;
  198. begin
  199.     Result := 0;
  200.     TargetFile := Info.AppValue;
  201.     case NotifyType of
  202.         ncCopyFile:             // Is this the file we want to extract?
  203.             if StrIComp (TargetFile, Info.FileName) = 0 then begin
  204.             Path := DestinationPath + ExtractFileDir (Info.FileName);
  205.             if not DirectoryExists (Path) then CreateDir (Path);
  206.             Result := _lcreat (PChar (DestinationPath + StrPas (Info.FileName)), 0);
  207.             Exit;
  208.             end;
  209.  
  210.         ncCloseFileInfo:        // Is this the file we want to close?
  211.             begin
  212.                 Result := 1;
  213.                 // This check is probably redundant, cos we only decompressed
  214.                 // a single file but let's play safe....
  215.                 if StrIComp (TargetFile, Info.FileName) = 0 then begin
  216.                     DosDateTimeToFileTime (Info.Date, Info.Time, FileTime);
  217.                     SetFileTime (Info.fd, Nil, Nil, @FileTime);
  218.                     MyClose (Info.fd);
  219.                 end;
  220.             end;
  221.     end;
  222. end;
  223.  
  224. procedure CABExtractFile (const CABFileName, DestPath, FileName: String);
  225. var
  226.     Context: THandle;
  227. begin
  228.     if CABIsFile (CABFileName) then begin
  229.         Context := NewFDIContext;
  230.         if Context <> 0 then try
  231.             DestinationPath := DestPath;
  232.             if DestinationPath [Length (DestinationPath)] <> '\' then DestinationPath := DestinationPath + '\';
  233.             FDICopy (Context, PChar (ExtractFileName (CABFileName)),
  234.                               PChar (ExtractFilePath (CABFileName)),
  235.                               0, @FileExtractSingleCallback, Nil, PChar (FileName));
  236.         finally
  237.             FDIDestroy (Context);
  238.         end;
  239.     end;
  240. end;
  241.  
  242. function FileExtractMultipleCallback (NotifyType: Integer; var Info: TFDINotification): Integer; cdecl;
  243. var
  244.     Path: String;
  245.     List: TStringList;
  246.     FileTime: TFileTime;
  247. begin
  248.     Result := 0;
  249.     List := Info.AppValue;
  250.     case NotifyType of
  251.         ncCopyFile:             // Do we want to extract this file?
  252.             if List.IndexOf (Info.FileName) <> -1 then begin
  253.             Path := DestinationPath + ExtractFileDir (Info.FileName);
  254.             if not DirectoryExists (Path) then CreateDir (Path);
  255.             Result := _lcreat (PChar (DestinationPath + StrPas (Info.FileName)), 0);
  256.             Exit;
  257.             end;
  258.  
  259.         ncCloseFileInfo:        // Do we want to close this file?
  260.             begin
  261.                 Result := 1;
  262.                 if List.IndexOf (Info.FileName) <> -1 then begin
  263.                     DosDateTimeToFileTime (Info.Date, Info.Time, FileTime);
  264.                     SetFileTime (Info.fd, Nil, Nil, @FileTime);
  265.                     MyClose (Info.fd);
  266.                 end;
  267.             end;
  268.     end;
  269. end;
  270.  
  271. procedure CABExtractMultipleFiles (const CABFileName, DestPath: String; List: TStringList);
  272. var
  273.     Context: THandle;
  274. begin
  275.     if CABIsFile (CABFileName) then begin
  276.         Context := NewFDIContext;
  277.         if Context <> 0 then try
  278.             DestinationPath := DestPath;
  279.             if DestinationPath [Length (DestinationPath)] <> '\' then DestinationPath := DestinationPath + '\';
  280.             FDICopy (Context, PChar (ExtractFileName (CABFileName)),
  281.                               PChar (ExtractFilePath (CABFileName)),
  282.                               0, @FileExtractMultipleCallback, Nil, List);
  283.         finally
  284.             FDIDestroy (Context);
  285.         end;
  286.     end;
  287. end;
  288.  
  289. // Load CABINET.DLL and get pointers to the various entry points...
  290.  
  291. procedure CABLoad;
  292. begin
  293.     CABLib := LoadLibrary ('cabinet.dll');
  294.     if CABLib = 0 then raise Exception.Create ('Can''t find CABINET.DLL');
  295.     @FDICreate    := GetProcAddress (CABLib, 'FDICreate');
  296.     @FDIDestroy   := GetProcAddress (CABLib, 'FDIDestroy');
  297.     @FDIIsCabinet := GetProcAddress (CABLib, 'FDIIsCabinet');
  298.     @FDICopy      := GetProcAddress (CABLib, 'FDICopy');
  299. end;
  300.  
  301. procedure CABUnload;
  302. begin
  303.     if CABLib <> 0 then FreeLibrary (CABLib);
  304. end;
  305.  
  306. initialization
  307.     CABLoad;
  308. finalization
  309.     CABUnload;
  310. end.
  311.